package obecto.utility
{
	import flash.system.ApplicationDomain;
	import flash.utils.Dictionary;
	import flash.utils.describeType;
	import flash.utils.getQualifiedClassName;
	import flash.utils.getQualifiedSuperclassName;
	
	import obecto.metadata.MetadataDirectives;
	import obecto.metadata.PartDirective;
	
	public class MetaUtility
	{
		private static var directivesByClassName : Dictionary = new Dictionary(true);
		
		public static function resolveMetadata(instance : Object) : MetadataDirectives
		{
			var className : String = getQualifiedClassName(instance);
			return directivesForClass(className);
		}
		
		private static function directivesForClass(className : String) : MetadataDirectives
		{
			var directives : MetadataDirectives = directivesByClassName[className];
			if (directives != null)
			{
				return directives;
			}
			
			directives = new MetadataDirectives();
			var domain : ApplicationDomain = ApplicationDomain.currentDomain; 
			var type : Class = domain.getDefinition(className) as Class;
			var baseClassName : String = getQualifiedSuperclassName(type);
			if (baseClassName != null)
			{
				var baseClassDirectives : MetadataDirectives = directivesForClass(baseClassName);
				directives.parts = baseClassDirectives.parts.concat();
			}
			
			var description : XML = describeType(type);
			resolveParts(description, directives.parts);
			
			directivesByClassName[className] = directives;
			return directives;
		}
		
		private static function resolveParts(description : XML, directives : Array) : void 
		{
			var metadata : XMLList = description.factory.metadata.(@name == "Part");
			for (var i : int = 0; i < metadata.length(); i++)
			{
				var directive : PartDirective = new PartDirective();
				directive.name = metadata[i].arg.(@key == "name").@value;
				directive.path = metadata[i].arg.(@key == "path").@value;
				directive.optional = metadata[i].arg.(@key == "optional").@value;
				directives.push(directive);
			}
			// Part-metadata tag can also be applied to accessors and valiables.
			// in such cases the name is taken from the name of the variable or accessor.
			metadata = description.factory.accessor.metadata.(@name == "Part");
			metadata += description.factory.variable.metadata.(@name == "Part");
			for (i = 0; i < metadata.length(); i++)
			{
				directive = new PartDirective();
				directive.name = metadata[i].parent.@name;
				directive.path = metadata[i].arg.(@key == "path").@value;
				directives.push(directive);
			}
		}

	}
}